package com.cloud.phone.utils

import android.app.Activity
import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.provider.Settings
import android.provider.Settings.SettingNotFoundException
import android.text.TextUtils.SimpleStringSplitter
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ShellUtils
import com.cloud.phone.bean.AppInfo
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import java.io.IOException
import java.io.InputStreamReader
import kotlin.system.exitProcess

/**
 * @auth: njb
 * @date: 2024/6/5 9:57
 * @desc: 系统属性工具类
 */
object SystemPropertyUtils {
    const val TAG = "SystemProperty"
    fun getProperty(key: String?, defaultValue: String?): String? {
        var value = defaultValue
        try {
            val c = Class.forName("android.os.SystemProperties")
            val get = c.getMethod("get", String::class.java, String::class.java)
            value = get.invoke(c, key, "unknown") as String
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            return value
        }
    }

    fun setProperty(key: String?, value: String?): Int {
        return try {
            val c = Class.forName("android.os.SystemProperties")
            val set = c.getMethod("set", String::class.java, String::class.java)
            set.invoke(c, key, value)
            0
        } catch (e: Exception) {
            e.printStackTrace()
            -1
        }
    }

    /**
     * 获取build.prop文件中的某个属性
     *
     * @param propName 属性名称
     * @return 属性值
     */
    fun getSystemProperty(propName: String): String? {
        val line: String
        var input: BufferedReader? = null
        try {
            val p = Runtime.getRuntime().exec("getprop $propName")
            input = BufferedReader(InputStreamReader(p.inputStream), 1024)
            line = input.readLine()
            input.close()
        } catch (ex: Exception) {
            LogUtils.e("Unable to read sysprop $propName", ex)
            return null
        } finally {
            if (input != null) {
                try {
                    input.close()
                } catch (e: Exception) {
                    LogUtils.e("Exception while closing InputStream", e)
                }
            }
        }
        //        LogUtils.i("getSystemProperty out time: " + System.currentTimeMillis());
        return line
    }

    /**
     * 杀死当前app进程
     *
     * @param activity
     */
    fun killAppProcess(activity: Activity) {
        //注意：不能先杀掉主进程，否则逻辑代码无法继续执行，需先杀掉相关进程最后杀掉主进程
        try {
            activity.finish()
            exitProcess(0)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun openNotificationSettingsForApp(context: Context) {
        // Links to this app's notification settings.
        val intent = Intent()
        intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS")
        intent.putExtra("app_package", context.packageName)
        intent.putExtra("app_uid", context.applicationInfo.uid)
        // for Android 8 and above
        intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName)
        context.startActivity(intent)
    }


    fun isAppRunning(context: Context, packageName: String): Boolean {
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        // 获取正在运行的应用程序进程列表
        val runningAppProcesses = activityManager.runningAppProcesses

        // 遍历进程列表，判断指定包名的应用是否在运行
        if (runningAppProcesses != null) {
            for (processInfo in runningAppProcesses) {
                if (processInfo.processName == packageName) {
                    return true
                }
            }
        }
        return false
    }

    //5.0以上，没有usage state权限
    const val AID_APP = 10000
    const val AID_USER = 100000

    /**
     * 5.0以上，没有usage state权限
     *
     * @return
     */
    fun getForegroundApp(): String? {
        val files = File("/proc").listFiles()
        var lowestOomScore = Int.MAX_VALUE
        var foregroundProcess: String? = null
        for (file in files) {
            if (!file.isDirectory()) {
                continue
            }
            var pid: Int
            pid = try {
                file.getName().toInt()
            } catch (e: NumberFormatException) {
                continue
            }
            try {
                val cgroup = read(String.format("/proc/%d/cgroup", pid))
                val lines = cgroup.split("\n".toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray()
                var cpuSubsystem: String
                var cpuaccctSubsystem: String
                if (lines.size == 2) { // 有的手机里cgroup包含2行或者3行，我们取cpu和cpuacct两行数据
                    cpuSubsystem = lines[0]
                    cpuaccctSubsystem = lines[1]
                } else if (lines.size == 3) {
                    cpuSubsystem = lines[0]
                    cpuaccctSubsystem = lines[2]
                } else {
                    continue
                }
                if (!cpuaccctSubsystem.endsWith(pid.toString())) {
                    // not an application process
                    continue
                }
                if (cpuSubsystem.endsWith("bg_non_interactive")) {
                    // background policy
                    continue
                }
                val cmdline = read(String.format("/proc/%d/cmdline", pid))
                //屏蔽掉你自己的其他后台进程
                if (cmdline.contains("com.cloud.phone")) {
                    continue
                }
                if (cmdline.contains("com.android.systemui")) {
                    continue
                }
                val uid = cpuaccctSubsystem.split(":".toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray()[2]
                    .split("/".toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray()[1].replace("uid_", "").toInt()
                if (uid >= 1000 && uid <= 1038) {
                    // system process
                    continue
                }
                var appId = uid - AID_APP
                var userId = 0
                // loop until we get the correct user id.
                // 100000 is the offset for each user.
                while (appId > AID_USER) {
                    appId -= AID_USER
                    userId++
                }
                if (appId < 0) {
                    continue
                }
                // u{user_id}_a{app_id} is used on API 17+ for multiple user
                // account support.
                // String uidName = String.format("u%d_a%d", userId, appId);
                val oomScoreAdj = File(
                    String.format(
                        "/proc/%d/oom_score_adj", pid
                    )
                )
                if (oomScoreAdj.canRead()) {
                    val oomAdj = read(
                        oomScoreAdj
                            .absolutePath
                    ).toInt()
                    if (oomAdj != 0) {
                        continue
                    }
                }
                val oomscore = read(
                    String.format(
                        "/proc/%d/oom_score", pid
                    )
                ).toInt()
                if (oomscore < lowestOomScore) {
                    lowestOomScore = oomscore
                    foregroundProcess = cmdline
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return foregroundProcess
    }

    @Throws(IOException::class)
    private fun read(path: String): String {
        val output = StringBuilder()
        val reader = BufferedReader(FileReader(path))
        output.append(reader.readLine())
        var line = reader.readLine()
        while (line != null) {
            output.append('\n').append(line)
            line = reader
                .readLine()
        }
        reader.close()
        return output.toString().trim { it <= ' ' } // 不调用trim()，包名后会带有乱码
    }

    fun autoAccessService(ct: Context, service: Class<*>) {
        LogUtils.d(TAG, "===包名为===$ct===服务为===$service")
        //辅助权限：如果root，先申请root权限
        if (AppUtils.isAppRoot()) {
            if (!hasServicePermission(ct, service)) {
                Thread { openServicePermissionRoot(ct, service) }.start()
            }
        } else {
            try {
                openServicePermission(ct, service)
            } catch (e: Exception) {
                e.printStackTrace()
                if (!hasServicePermission(ct, service)) {
                    jumpSystemSetting(ct)
                }
            }
        }
    }

    fun jumpSystemSetting(ct: Context) {
        // jump to setting permission
        val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        ct.startActivity(intent)
    }

    fun hasServicePermission(ct: Context, serviceClass: Class<*>): Boolean {
        var ok = 0
        try {
            ok = Settings.Secure.getInt(ct.contentResolver, Settings.Secure.ACCESSIBILITY_ENABLED)
        } catch (e: SettingNotFoundException) {
            e.printStackTrace()
        }
        val ms = SimpleStringSplitter(':')
        if (ok == 1) {
            val settingValue = Settings.Secure.getString(
                ct.contentResolver,
                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
            )
            if (settingValue != null) {
                ms.setString(settingValue)
                while (ms.hasNext()) {
                    val accessibilityService = ms.next()
                    if (accessibilityService.contains(serviceClass.getSimpleName())) {
                        return true
                    }
                }
            }
        }
        return false
    }

    fun openServicePermission(ct: Context, serviceClass: Class<*>) {
        val enabledServices = getEnabledServicesFromSettings(ct, serviceClass) ?: return
        val toggledService =
            ComponentName.unflattenFromString(ct.packageName + "/" + serviceClass.getName())
        val accessibilityEnabled = true
        enabledServices.add(toggledService)
        // Update the enabled services setting.
        val enabledServicesBuilder = StringBuilder()
        for (enabledService in enabledServices) {
            enabledServicesBuilder.append(enabledService!!.flattenToString())
            enabledServicesBuilder.append(":")
        }
        val enabledServicesBuilderLength = enabledServicesBuilder.length
        if (enabledServicesBuilderLength > 0) {
            enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1)
        }
        Settings.Secure.putString(
            ct.contentResolver,
            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
            enabledServicesBuilder.toString()
        )
        // Update accessibility enabled.
        Settings.Secure.putInt(
            ct.contentResolver,
            Settings.Secure.ACCESSIBILITY_ENABLED,
            if (accessibilityEnabled) 1 else 0
        )
    }

    fun getEnabledServicesFromSettings(
        context: Context,
        serviceClass: Class<*>
    ): MutableSet<ComponentName?>? {
        var enabledServicesSetting = Settings.Secure.getString(
            context.contentResolver,
            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
        )
        if (enabledServicesSetting == null) {
            enabledServicesSetting = ""
        }
        val enabledServices: MutableSet<ComponentName?> = HashSet()
        val colonSplitter = SimpleStringSplitter(':')
        colonSplitter.setString(enabledServicesSetting)
        while (colonSplitter.hasNext()) {
            val componentNameString = colonSplitter.next()
            val enabledService = ComponentName.unflattenFromString(componentNameString)
            if (enabledService != null) {
                if (enabledService.flattenToString().contains(serviceClass.getSimpleName())) {
                    return null
                }
                enabledServices.add(enabledService)
            }
        }
        return enabledServices
    }

    private fun openServicePermissionRoot(ct: Context, service: Class<*>) {
        val cmd1 =
            "settings put secure enabled_accessibility_services  " + ct.packageName + "/" + service.getName()
        val cmd2 = "settings put secure accessibility_enabled 1"
        val cmds = arrayOf(cmd1, cmd2)
        ShellUtils.execCmd(cmds, true)
    }
}